#include "ast.hpp"

declaration::declaration(string scalar_variable_name) 
:	is_array(false), name(scalar_variable_name), array_size(0) {}

declaration::declaration(string array_name, int size)
:	is_array(true), name(array_name), array_size(size) 
{
	if (size <= 0) {
		cerr << "Array " << array_name << " can not have zero or negative size!" << endl;
		abort();
	}
}

symbol_table::symbol_table(vector<declaration*> decls) {
	for_each(decls, [this](declaration* d) {
		bool present = this->declarations.count(d->name);
		if (present) {
			cerr << "Variable or array " << d->name << " defined twice!" << endl;
			abort();
		} else {
			this->declarations[d->name] = d;
		}
	});
}


ASTnode::ASTnode() 
:	parent(NULL) {}

b_not::b_not(bexp *_arg) 
:	arg(_arg) 
{
	this->arg->parent = this;
}

b_logical::b_logical(bexp* _arg1, bexp* _arg2) 
:	arg1(_arg1), arg2(_arg2) 
{
	this->arg1->parent = this;
	this->arg2->parent = this;
}

b_and::b_and(bexp *_arg1, bexp *_arg2) : b_logical(_arg1, _arg2) {}
b_or::b_or(bexp *_arg1, bexp *_arg2) : b_logical(_arg1, _arg2) {}

b_relational::b_relational(arith *_arg1, arith *_arg2)
:	arg1(_arg1), arg2(_arg2)
{
	this->arg1->parent = this;
	this->arg2->parent = this;
}

b_gt::b_gt(arith* _arg1, arith* _arg2) : b_relational(_arg1, _arg2) {}
b_ge::b_ge(arith* _arg1, arith* _arg2) : b_relational(_arg1, _arg2) {}
b_lt::b_lt(arith* _arg1, arith* _arg2) : b_relational(_arg1, _arg2) {}
b_le::b_le(arith* _arg1, arith* _arg2) : b_relational(_arg1, _arg2) {}
b_eq::b_eq(arith* _arg1, arith* _arg2) : b_relational(_arg1, _arg2) {}
b_neq::b_neq(arith* _arg1, arith* _arg2) : b_relational(_arg1, _arg2) {}



variable::variable(string _name) {this->name = _name;}

array_element::array_element(string _array_name, arith *_index) 
:	index(_index) 
{
	this->name = _array_name;
	this->index->parent = this;
}

constant::constant(int _literal) :	literal(_literal) {}

unary_operator::unary_operator(arith *_arg) 
:	arg(_arg)
{
	this->arg->parent = this;
}

anegation::anegation(arith *_arg) : unary_operator(_arg) {}

binary_operator::binary_operator(arith *_arg1, arith *_arg2) 
:	arg1(_arg1), arg2(_arg2)
{
	this->arg1->parent = this;
	this->arg2->parent = this;
}

aplus::aplus(arith *_arg1, arith *_arg2) :	binary_operator(_arg1, _arg2) {}
aminus::aminus(arith *_arg1, arith *_arg2) :	binary_operator(_arg1, _arg2) {}
amultiply::amultiply(arith *_arg1, arith *_arg2) :	binary_operator(_arg1, _arg2) {}
adivide::adivide(arith *_arg1, arith *_arg2) :	binary_operator(_arg1, _arg2) {}


sequence::sequence(vector<statement*> _statements)
:	statements(_statements)
{
	for (vector<statement*>::iterator it = statements.begin(); it != statements.end(); ++it) {
		(*it)->parent = this;
	}
}

assignment::assignment(lvalue *_lhs, arith *_rhs) 
:	rhs(_rhs) 
{
	this->dest = _lhs;
	this->dest->parent = this;
	this->rhs->parent = this;
}

s_skip::s_skip() {};

s_read::s_read(lvalue *_dest)
{
	this->dest = _dest;
	this->dest->parent = this;
}

s_write::s_write(arith *_arg) 
:	arg(_arg)
{
	this->arg->parent = this;
}

s_if::s_if(bexp *_cond, statement *_s_true, statement *_s_false)
:	s_true(_s_true), s_false(_s_false)
{
	this->cond = _cond;
	this->cond->parent = this;
	this->s_true->parent = this;
	this->s_false->parent = this;
}

s_while::s_while(bexp *_cond, statement *_body)
:	body(_body)
{
	this->cond = _cond;
	this->cond->parent = this;
	this->body->parent = this;
}

program::program(statement *_body, symbol_table* _sym_tab)
:	body(_body), sym_tab(_sym_tab)
{
	this->parent = NULL;	// program has no parent
	this->body->parent = this;
	
	// If body is a single if or while, we need to sorround it by skip statements
	// So we first need to wrap a sequence around
	if (dynamic_cast<s_if*>(this->body) || dynamic_cast<s_while*>(this->body)) {
		vector<statement*> v;
		v.push_back(this->body);
		sequence *c = new sequence(v);
		this->body->parent = c;
		this->body = c;
		this->body->parent = this;
	}	
	
	// Add initial and final skip's as nessecary
	if (sequence *c = dynamic_cast<sequence*>(this->body)) {
		assert(!c->statements.empty());

		statement *f = c->statements.front();
		if (dynamic_cast<s_if*>(f) || dynamic_cast<s_while*>(f)) {
			s_skip *ny = new s_skip();
			ny->parent = c;
			c->statements.insert(c->statements.begin(), ny);
		}
		
		statement *b = c->statements.back();
		if (dynamic_cast<s_if*>(b) || dynamic_cast<s_while*>(b)) {
			s_skip *ny = new s_skip();
			ny->parent = c;
			c->statements.push_back(ny);
		}
	}
}


void b_true			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_false		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_not			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_and			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_or			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_gt			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_ge			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_lt			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_le			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_eq			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void b_neq			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}

void variable		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void array_element	::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void constant		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void anegation		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void aplus			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void aminus			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void amultiply		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void adivide		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}

void sequence		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void assignment		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void s_skip			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void s_read			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void s_write		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void s_if			::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void s_while		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}
void program		::acceptVisit(ASTvisitor *visitor) {visitor->visit(this);}


std::ostream& operator<<(std::ostream& stream, ASTnode *n)
{
	ASTvisitorString str;
	stream << str.convert(n);
	return stream; 
}
